<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>promise</title>
</head>
<body>
<script>
    /*
    ① 其实就是一个构造函数
    ② 每个函数对象都有一个原型属性(prototype),属性值本质上就是一个普通的对象
    ③ 每个对象的__proto__指向构造函数的原型对象(prototype)
    ④ 原型对象上有constrctor属性,指向构造函数
* */

    function Fun() {
        // this.run = 2 //第2个找
        this.aa = 2 //第2个找
    }
    console.log(Fun.prototype);
    Fun.prototype.run = 4  //第四个找
    var fun = new Fun();
    // fun.run = 1;  //第一个找
    // fun.run2 = 3;  //第一个找
    fun.__proto__.run = 3;  //第三个找
    Fun.prototype.run = 4  //第四个找
   // Object.__proto__.run = 5 ; //第五个找
    console.log(fun.run);
    console.log(fun.aa);
    console.log(Object.__proto__.run);
    console.log(fun);

    /*
    *
    *   原形是用来解决共享属性和共享方法的
        原型链最终的指向是Object的prototype, 而Object中的__proto__是null
        查找顺序: 对象本身查找==>构造函数中查找==>对象原形==>构造函数的原形中==>当前原形的原形中查找
        *
        *
        *

* */

    const PENDING = 'pending';
    const FULFILLED= 'fulfilled';
    const REJECTED= 'rejected';


    class MyPromise {
        // 私有属性外部不让用，用新出来的#前缀修饰
        //用 typescript 可以用 private
        // 默认是pedding状态
        #state = PENDING;

        // 定义结果,默认是undefined
        #result = undefined;

        // 保存当前then方法里面的方法地址
        #currentThenParams = [];

        // executor 是外部传入的
        constructor(executor) {
            // 解决 目录1.1 报错问题
            if (typeof executor !== 'function') {
                throw new TypeError(`Promise resolver ${executor} is not a function`)
            }
            // 外部会调用方法并传入参数，不传就是undefind
            const resolve= (data) => {
                this.#updateState(FULFILLED, data)
            }
            const reject= (error) => {
                this.#updateState(REJECTED, error)
            }
            /* 解决 内部报错问题 用try catch 包裹
        new Promise(() => {
            throw 123
          })
        */
            // 注意：这里只能捕捉到同步错误，内部如果是setTimeout 返回的异步代码是捕捉不到的
            try {
                executor(resolve, reject)
            } catch (error) {
                // 报错 内部调用reject方法
                reject(error)
            }
        }

        // 传入状态和 结果// 统一入口，下面会用到
        #updateState (state, result) {
            if (this.#state !== PENDING) return;
            this.#state = state;
            this.#result = result;
            this.#handlerThenFn()
        }

        // 判断是否是一个类promise
        // 判断不管你是一个对象还是一个方法，只要带有then方法，就可以互操作
        #isPromiseLike(result) {
            if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
                if (typeof result.then === 'function') {
                    return true
                }
            }
            return false
        }

        #joinToMicroTask(fn) {
            // 假如是node环境 判断 process
            if (typeof process === 'object' && typeof process.nextTick === 'function') {
                process.nextTick(fn)
            } else if (typeof MutationObserver === 'function') { // 判断是浏览器环境, 用 MutationObserver 实现
                // 注册一个观察器
                const observer = new MutationObserver(fn);
                // 定义一个文本节点, 初始值为1
                const textNode= document.createTextNode('1');
                // 观察文本节点的变化
                observer.observe(textNode, {
                    characterData: true
                })
                textNode.data = '2'
            } else {
                alert(1)
            }
        }

        #handlerThenFn () {
            // 当前状态没有变更就不执行
            if (this.#state === PENDING) return;
            while (this.#currentThenParams.length) {
                // 每次弹出一个去执行
                const { onFulfilled,
                    onRejected,
                    resolve,
                    reject } = this.#currentThenParams.shift();

                this.#joinToMicroTask(() => {
                    if (this.#state === FULFILLED) {
                        // onFulfilled 可以是任意值，需要判断是否是函数
                        if (typeof onFulfilled === 'function') {
                            const result =  onFulfilled(this.#result);
                            if (this.#isPromiseLike(result)) {
                                result.then(resolve, reject)
                            } else {
                                resolve(result)
                            }
                        } else {
                            // 走到这里说明传进来的是其他值，应该和resolve返回一致 的结果
                            resolve(this.#result)
                        }
                    } else if (this.#state === REJECTED) {
                        // onRejected可以是任意值，需要判断是否是函数
                        if (typeof onRejected=== 'function') {
                            const result = onRejected(this.#result)
                            resolve(result)
                        } else {
                            // 走到这里说明传进来的是其他值，应该和resolve返回一致 的结果
                            reject(this.#result)
                        }
                    }
                })
            }
        }

        // 有2个参数 onFulfilled 和 onRejected
        then (onFulfilled, onRejected) {
            // 返回一个新的promise,
            return new MyPromise((resolve, reject) => {
                // 把所有方法的地址存起来
                this.#currentThenParams.push({
                    onFulfilled,
                    onRejected,
                    resolve,
                    reject
                });
                this.#handlerThenFn()
            })
        }

        // 直接效用then方法即可
        catch (onRejected) {
            return this.then(undefined, onRejected)
        }

        // finally
        finally (onFinally) {
            return this.then((data) => {
                // 这里没有参数的
                onFinally();
                // 返回的状态与原始promise状态一致，直接把当前data返回
                return data;
            }, (error) => {
                onFinally();
                // 返回的状态与原始promise状态一致，抛个错, 这一步会被上面try catch 捕捉到
                throw error
            })
        }

        static resolve (data) {
            // 如果是我们自己写的Oromise, 直接返回即可
            if (data instanceof MyPromise) return data;
            // 由于静态方法里面无法获取实例方法, 那我们创建一个Promise
            let _resolve, _reject;
            const p = new MyPromise((resolve, reject) => {
                _resolve = resolve;
                _reject = reject;
            })
            // 此时p 就是一个实例
            if (p.#isPromiseLike(data)) {
                data.then(_resolve, _reject)
            } else {
                _resolve(data)
            }
            return p
        }

        static reject (error) {
            return new MyPromise((resolve, reject) => {
                // 不传传进来什么，我都拒绝
                reject(error)
            })
        }

        // 接受一个可迭代对象
        static all (iteratorObj) {
            // 判断是否可迭代，不可迭代抛错
            if (!(iteratorObj != null && typeof iteratorObj[Symbol.iterator] === 'function')) {
                throw new TypeError(`${iteratorObj} is not iterable (cannot read property Symbol(Symbol.iterator))`)
            }

            // 首先需要一个Promise，并且最终会被返回
            let _resolve, _reject; // 先不管，先保存下来
            const p = new MyPromise((resolve, reject) => {
                _resolve = resolve;
                _reject = reject;
            })
            // 可迭代对象肯定能被for of 遍历，但拿不到下标，需要外部定义一个下标，在遍历的时候手动维护
            // promise的返回值只有执行完成后才能拿到，定义一个数组，用于保存成功的promise返回值
            let count = 0;
            let successCount = 0; // 成功的次数
            const successArr = [];
            for (const item of iteratorObj) {
                console.log(item);
                // 因为promise是异步的，不知道啥时候返回，不能使用count作为下标
                // 作用域种保存当前promise执行后需要塞到数组下标的那个位置
                const index = count;
                count++;
                // 先变成一个promise
                MyPromise.resolve(item).then(data => {
                    // 把数据塞到保存的对应下标的位置
                    successArr[index] = data;
                    successCount++
                    // 当所有的promise 全都完成的时候就节俗promise
                    // 也就是成功的数组长度=== count， bindqie
                    if (successCount === count) {
                        _resolve(successArr)
                    }
                }, error => {
                    // 只要有一个失败了，那就借宿了
                    _reject(error)
                })
            }
            // 如果传入的对象没有什么可以迭代 ，那就返回空数组
            if (count === 0) {
                _resolve([])
            }
            return p
        }

    }

    export default MyPromise


</script>
</body>
</html>
